import {
  useRef,
  useState,
  forwardRef,
  Ref,
  useImperativeHandle,
  useCallback,
  useMemo,
  ReactNode,
} from 'react';
import {
  Form,
  Radio,
  Input,
  Tabs,
  RadioChangeEvent,
  Button,
  Modal,
  message,
  Tooltip,
} from 'antd';
import { uuid } from '@inbiz/utils';
import AceEditor from 'react-ace';
import { useUpdateEffect } from 'ahooks';
import InputTable, { IValue, IType, IInputTable } from './InputTable';
import OutTable, { IOutTableValue, IOutTable } from './OutTable';
import './index.less';
import 'ace-builds/src-noconflict/mode-javascript';
import 'ace-builds/src-noconflict/mode-css';
import 'ace-builds/src-noconflict/theme-tomorrow';
import titleSvg from '../svg/title.svg';
import { ExclamationCircleOutlined } from '@ant-design/icons';
export type ICustomApiValue = {
  method?: 'GET' | 'POST' | 'DELETE' | 'PUT';
  url?: string;
  params?: IValue;
  headers?: IValue;
  body?: IValue | string;
  contentType?:
    | 'application/x-www-form-urlencoded'
    | 'application/json'
    | 'multipart/form-data';
  result?: IOutTableValue;
};

export type ICustomApi = {
  getValue: () => Promise<ICustomApiValue>;
};

function CustomApi(
  {
    value,
    isPrimaryKey,
    resultKeys,
    isShow,
    placeholder = '支持js语法，如${data.appInfo.xxx}，其中data为所有的参数集合',
    inputTooltip,
    isController
  }: {
    value?: ICustomApiValue;
    resultKeys?: string[];
    isPrimaryKey?: boolean;
    isShow: boolean;
    placeholder?: string
    inputTooltip?: ReactNode
    isController?: boolean
  },
  ref: Ref<ICustomApi>,
) {
  const [form] = Form.useForm();
  const method = Form.useWatch('method', form);
  const [apiData, $apiData] = useState(() => updateValue(value));
  const [bodyType, $bodyType] = useState<'list' | 'json'>(() =>
    typeof apiData.body === 'string' ? 'json' : 'list',
  );
  const [activeKey, $activeKey] = useState('params');
  const paramsRef = useRef<IInputTable>(null);
  const headersRef = useRef<IInputTable>(null);
  const bodyRef = useRef<IInputTable>(null);
  const outRef = useRef<IOutTable>(null);

  useImperativeHandle(
    ref,
    () => ({
      getValue: async () => {
        try {
          await form.validateFields();
          const formData = form.getFieldsValue();
          const result = await outRef.current?.getValue();
          const params = paramsRef.current?.getValue();
          const headers = headersRef.current?.getValue();
          const body =
            bodyType === 'list' ? bodyRef.current?.getValue() : apiData.body;

          if (typeof body === 'string') {
            try {
              JSON.parse(body);
            } catch {
              message.error('Body格式输入错误，请重新输入');
              $activeKey('body');
              return Promise.reject();
            }
          }
          return {
            ...formData,
            result,
            params,
            headers,
            body,
            contentType: apiData.contentType,
          };
        } catch (e) {
          return Promise.reject();
        }
      },
    }),
    [apiData.contentType, bodyType, apiData.body],
  );

  useUpdateEffect(() => {
    $apiData(updateValue(value));
  }, [value]);

  const handleContentTypeChange = useCallback((event: RadioChangeEvent) => {
    $apiData((data) => ({
      ...data,
      contentType: event.target.value,
      data: updateValue(undefined, event.target.value).body,
    }));
  }, []);

  const bodyOption = useMemo(() => {
    return {
      rootOption:
        apiData.contentType === 'application/json'
          ? (['object', 'array'] as IType)
          : undefined,
      typeOption:
        apiData.contentType === 'application/json'
          ? undefined
          : (['string', 'boolean', 'number', 'any'] as IType),
    };
  }, [apiData.contentType]);

  useUpdateEffect(() => {
    $apiData((api) => {
      return {
        ...api,
        body:
          bodyType == 'json' ? '' : updateValue({}, apiData.contentType).body,
      };
    });
  }, [apiData.contentType, bodyType]);
  const handleBodyTypeChange = useCallback(() => {
    Modal.confirm({
      title: '提示',
      content: '是否切换模式，切换后Body内容会被清空',
      okText: '确定',
      cancelText: '取消',
      onOk: () => {
        $apiData((old) => {
          if (bodyType === 'list') {
            return {
              ...old,
              body: '',
            };
          } else {
            return {
              ...old,
              body: updateValue(undefined, old.contentType).body,
            };
          }
        });
        $bodyType(bodyType === 'json' ? 'list' : 'json');
      },
    });
  }, [bodyType]);
  return (
    <div
      className="customApiOption"
      style={{ display: isShow ? 'block' : 'none', maxHeight: '512px', overflowY: 'auto' }}
    >
      <div className="customApiOption-title">
        <img src={titleSvg} />
        Request
      </div>
      <Form
        form={form}
        initialValues={{
          method: value?.method || 'POST',
          url: value?.url || '',
        }}
        labelAlign="left"
        colon={false}
      >
        <Form.Item label="请求类型" name="method" required>
          <Radio.Group>
            <Radio value="GET">GET</Radio>
            <Radio value="POST">POST</Radio>
            <Radio value="DELETE">DELETE</Radio>
            <Radio value="PUT">PUT</Radio>
          </Radio.Group>
        </Form.Item>
        <Form.Item label="请求链接" name="url" required>
          <Input
            placeholder={placeholder}
            suffix={
              inputTooltip !== false && <Tooltip
                color={'#fff'}
                overlayInnerStyle={{ width: 300 }}
                className="url_iconBox"
                title={
                  <div style={{ color: '#000' }}>
                    {placeholder}
                  </div>
                }
              >
                <ExclamationCircleOutlined />
              </Tooltip>
            }
          />
        </Form.Item>
      </Form>
      <Tabs activeKey={activeKey} onChange={(key) => $activeKey(key)}>
        <Tabs.TabPane tab="Params" key="params" forceRender>
          <InputTable
            value={apiData.params}
            typeOption={['string', 'boolean', 'number', 'any']}
            scroll={{ y: 232 }}
            ref={paramsRef}
            tooltip={inputTooltip}
            isController={isController}
          />
        </Tabs.TabPane>
        <Tabs.TabPane tab="Header" key="headers" forceRender>
          <InputTable
            value={apiData.headers}
            typeOption={['string', 'boolean', 'number', 'any']}
            scroll={{ y: 232 }}
            ref={headersRef}
            tooltip={inputTooltip}
            isController={isController}
          />
        </Tabs.TabPane>
        {method != 'GET' && (
          <Tabs.TabPane tab="Body" key="body" forceRender>
            <div className="contentTypeBox">
              <span>
                <span className="required">*</span>Content-Type:
              </span>
              <Radio.Group
                defaultValue={apiData.contentType}
                onChange={handleContentTypeChange}
              >
                <Radio value="application/x-www-form-urlencoded">
                  application/x-www-form-urlencoded
                </Radio>
                <Radio value="application/json">application/json</Radio>
                <Radio value="multipart/form-data">multipart/form-data</Radio>
              </Radio.Group>
              <Button type="primary" onClick={handleBodyTypeChange}>
                {bodyType === 'json' ? 'List' : 'JSON'}
              </Button>
            </div>
            {bodyType === 'list' ? (
              <InputTable
                value={apiData.body as IValue}
                rootOption={bodyOption.rootOption}
                typeOption={bodyOption.typeOption}
                scroll={{ y: 200 }}
                ref={bodyRef}
                tooltip={inputTooltip}
                isController={isController}
              />
            ) : (
              <AceEditor
                theme="tomorrow"
                mode="javascript"
                name="beforeCode"
                width="100%"
                height="300px"
                value={apiData.body as string}
                fontSize={14}
                onChange={(value) =>
                  $apiData((old) => ({ ...old, body: value }))
                }
                placeholder="请求的JSON内容, value值同样支持参数内容的语法"
              />
            )}
          </Tabs.TabPane>
        )}
      </Tabs>
      {resultKeys?.length && (
        <>
          <div className="customApiOption-title">
            <img src={titleSvg} />
            Response
          </div>
          <OutTable
            keys={resultKeys}
            isPrimaryKey={isPrimaryKey}
            ref={outRef}
            value={value?.result}
          />
        </>
      )}
    </div>
  );
}

export default forwardRef(CustomApi);

function updateValue(
  value?: ICustomApiValue,
  contentType: string = 'application/json',
) {
  return {
    params: value?.params?.length
      ? value.params
      : [{ id: uuid(10, 8), type: 'string' as const, name: '', root: true }],
    headers: value?.headers?.length
      ? value.headers
      : [{ id: uuid(10, 8), type: 'string' as const, name: '', root: true }],
    body: value?.body?.length
      ? value.body
      : [
          {
            id: uuid(10, 8),
            type:
              contentType === 'application/json'
                ? 'object'
                : ('string' as const),
            name: contentType === 'application/json' ? '根节点' : '',
            root: true,
          },
        ],
    contentType: value?.contentType || 'application/json',
  };
}
